package com.orange.framework.consumer;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 只有jdk代理才有默认方法
 * 执行接口中的default函数
 * 此处所有代码均摘抄自MapperProxy
 */
public class DefaultMethodHandler {
    //
    private static final Constructor<MethodHandles.Lookup> lookupConstructor;

    private static final Method privateLookupInMethod;

    static {
        Method privateLookupIn;
        try {
            privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
        } catch (NoSuchMethodException var5) {
            privateLookupIn = null;
        }

        privateLookupInMethod = privateLookupIn;
        Constructor<MethodHandles.Lookup> lookup = null;
        if (privateLookupInMethod == null) {
            try {
                lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, Integer.TYPE);
                lookup.setAccessible(true);
            } catch (NoSuchMethodException var3) {
                throw new IllegalStateException("There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.", var3);
            } catch (Exception var4) {
                lookup = null;
            }
        }

        lookupConstructor = lookup;
    }

    interface MapperMethodInvoker {
        Object invoke(Object var1, Method var2, Object[] var3) throws Throwable;
    }


    public static DefaultMethodInvoker getInvoker(Method method){
        try {
            return privateLookupInMethod == null ?
                    new DefaultMethodInvoker(getMethodHandleJava8(method)) : new DefaultMethodInvoker(getMethodHandleJava9(method));

        } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException var4) {
            throw new RuntimeException(var4);
        }
    }

    public static class DefaultMethodInvoker implements MapperMethodInvoker {
        private final MethodHandle methodHandle;

        public DefaultMethodInvoker(MethodHandle methodHandle) {
            this.methodHandle = methodHandle;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return this.methodHandle.bindTo(proxy).invokeWithArguments(args);
        }
    }

    private static MethodHandle getMethodHandleJava9(Method method) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Class<?> declaringClass = method.getDeclaringClass();
        return ((MethodHandles.Lookup)privateLookupInMethod.invoke((Object)null, declaringClass, MethodHandles.lookup())).findSpecial(declaringClass, method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes()), declaringClass);
    }

    private static MethodHandle getMethodHandleJava8(Method method) throws IllegalAccessException, InstantiationException, InvocationTargetException {
        Class<?> declaringClass = method.getDeclaringClass();
        return ((MethodHandles.Lookup)lookupConstructor.newInstance(declaringClass, 15)).unreflectSpecial(method, declaringClass);
    }

}
